/**
 * 
 */
package org.ranch.web.controllers;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.ranch.api.security.service.AuthenticationService;
import org.ranch.api.service.ConceptService;
import org.ranch.api.service.FarmService;
import org.ranch.model.Animal;
import org.ranch.model.Concept;
import org.ranch.model.Farm;
import org.ranch.model.Kraal;
import org.ranch.model.User;
import org.ranch.model.exception.RanchValidationException;
import org.ranch.web.SendDataToMobileApp;
import org.ranch.web.midlet.MidletAnimal;
import org.ranch.web.midlet.MidletPrepareAnimal;
import org.ranch.web.midlet.MidletUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @author Jmpango
 * 
 */
@Controller
@RequestMapping("midlet")
public class MidletController extends SendDataToMobileApp {
    @Autowired
    private AuthenticationService authenticationService;

    @Autowired
    private ConceptService conceptService;

    @Autowired
    private FarmService farmService;

    private Logger log = LoggerFactory.getLogger(MidletController.class);
    InputStream in = null;
    ByteArrayOutputStream bout = null;

    @RequestMapping(method = RequestMethod.POST)
    public void getLoginDataFromMidlet(HttpServletRequest request, HttpServletResponse response)
	    throws IOException, ClassNotFoundException {
	byte[] inData = null;
	/*
	 * Get data sent from the midlet
	 */
	inData = extractMidletData(request);

	MidletUser midlet = new MidletUser();
	midlet.deserializer(inData);

	User user = authenticationService.authenticateMobileUser(midlet.getUsername(),
		midlet.getPassword());

	if (user != null) {
	    System.out.println("Mobile user logged in successfully ....");
	    byte[] outData = new MidletPrepareAnimal("Yes", conceptService).serializer();

	    /*
	     * Sending back to the midlet
	     */
	    SendDataBackToMidlet(response, outData);
	    log.info("User logged in successfully ....");
	} else {
	    MidletUser midletUser = new MidletUser("wrong", "wrong", "No");
	    byte[] outData = midletUser.serializer();

	    /*
	     * Sending back to the midlet
	     */
	    SendDataBackToMidlet(response, outData);
	    System.out.println("User access denied ....");
	}
    }

    @RequestMapping(value = "/content", method = RequestMethod.POST)
    public void getAnimalEarTagDataFromMidlet(HttpServletRequest request,
	    HttpServletResponse response)
	    throws IOException, ClassNotFoundException {
	Animal systemAnimal = farmService.getAnimalByEarTag(getInputData(request));

	if (systemAnimal != null) {
	    MidletAnimal midletAnimal = copySystemAnimalToMidletAnimal(systemAnimal);
	    byte[] outData = midletAnimal.serializer();
	    SendDataBackToMidlet(response, outData);
	} else {
	    SendDataBackToMidlet(response, writeIntoBytes("No").toByteArray());
	}

    }

    /**
     * @return
     * @throws IOException
     */
    private ByteArrayOutputStream writeIntoBytes(String text) throws IOException {
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	DataOutputStream dos = new DataOutputStream(baos);
	dos.writeUTF(text);
	dos.flush();
	dos.close();
	baos.close();
	return baos;
    }

    @RequestMapping(value = "/saveEditAnimal", method = RequestMethod.POST)
    public void getSaveEditAnimal(HttpServletRequest request,
	    HttpServletResponse response)
	    throws IOException, ClassNotFoundException {
	byte[] inData = null;

	inData = extractMidletData(request);

	ByteArrayInputStream bais = new ByteArrayInputStream(inData);
	DataInputStream dis = new DataInputStream(bais);

	String animalID = dis.readUTF().trim();
	String otherData = dis.readUTF();
	Animal newAnimal = getAnimal(extractToList(otherData.getBytes()));

	if (newAnimal.getKraal() != null) {
	    try {
		Animal oldAnimal = new Animal();

		if (StringUtils.isEmpty(animalID)) {
		    newAnimal.setId(null);
		    oldAnimal = newAnimal;
		} else {
		    oldAnimal = farmService.getAnimalById(animalID);
		    newAnimal.setId(animalID);

		    if (newAnimal.getDateOfBirth() == null) {
			newAnimal.setDateOfBirth(oldAnimal.getDateOfBirth());
		    }

		    if (newAnimal.getPurchaseDate() == null) {
			newAnimal.setPurchaseDate(oldAnimal.getPurchaseDate());
		    }

		    copyAnimal(oldAnimal, newAnimal);
		}

		farmService.save(oldAnimal);
		SendDataBackToMidlet(response, writeIntoBytes("yes").toByteArray());
	    } catch (RanchValidationException e) {
		e.printStackTrace();
	    }
	} else {
	    SendDataBackToMidlet(response, writeIntoBytes("kraal").toByteArray());
	}

	bais.reset();
	dis.close();
	bais.close();

    }

    private void copyAnimal(Animal oldAnimal, Animal newAnimal) {
	oldAnimal.setId(newAnimal.getId());
	oldAnimal.setType(newAnimal.getType());
	oldAnimal.setEarTag(newAnimal.getEarTag());
	oldAnimal.setWeight(newAnimal.getWeight());
	oldAnimal.setName(newAnimal.getName());
	oldAnimal.setHealthStatus(newAnimal.getHealthStatus());
	oldAnimal.setDateOfBirth(newAnimal.getDateOfBirth());
	oldAnimal.setColour(newAnimal.getColour());
	oldAnimal.setKraal(newAnimal.getKraal());
	oldAnimal.setPurchaseDate(newAnimal.getPurchaseDate());
	oldAnimal.setDescription(newAnimal.getDescription());
	oldAnimal.setBreed(newAnimal.getBreed());
	oldAnimal.setBirthWeight(newAnimal.getBirthWeight());
	oldAnimal.setBuyingPrice(newAnimal.getBuyingPrice());
	oldAnimal.setChildren(newAnimal.getChildren());
	oldAnimal.setBirthWeight(newAnimal.getBirthWeight());

    }

    private Animal getAnimal(List<String> element) {
	Animal newAnimal = new Animal();
	if (element != null) {
	    newAnimal.setEarTag(element.get(0));
	    newAnimal.setWeight(Integer.parseInt(element.get(1).trim()));
	    newAnimal.setName(element.get(2));
	    newAnimal.setKraal(getKraal(element.get(3).trim()));
	    newAnimal.setBirthWeight(Integer.parseInt(element.get(4).trim()));
	    newAnimal.setSellingPrice(Integer.parseInt(element.get(5).trim()));
	    newAnimal.setBuyingPrice(Integer.parseInt(element.get(6).trim()));

	    newAnimal.setType(getConcept(element.get(7).trim()));
	    newAnimal.setHealthStatus(getConcept(element.get(8).trim()));
	    newAnimal.setColour(getConcept(element.get(9).trim()));
	    newAnimal.setBreed(getConcept(element.get(10).trim()));
	    newAnimal.setStatus(getConcept(element.get(11).trim()));

	    if (StringUtils.isNotBlank(element.get(12)) && element.get(12) != null) {
		newAnimal.setDateOfBirth(getDate(element.get(12)));
	    }

	    if (StringUtils.isNotBlank(element.get(13)) && element.get(13) != null) {
		newAnimal.setPurchaseDate(getDate(element.get(13)));
	    }

	}
	return newAnimal;
    }

    private Kraal getKraal(String name) {
	return farmService.getKraalByName(name);
    }

    private Date getDate(String dateString) {
	if (dateString.equals("null")) {
	    return null;
	} else {
	    int month = Integer.parseInt(getMonth(dateString.substring(4, 7).trim()));
	    int day = Integer.parseInt(dateString.substring(8, 10).trim());
	    int year = Integer.parseInt(dateString.substring(dateString.length() - 4,
			dateString.length()).trim());
	    String date = year + "/" + month + "/" + day;
	    SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
	    Date d = null;
	    try {
		d = formatter.parse(date);
	    } catch (ParseException e) {
		e.printStackTrace();
	    }
	    return d;
	}
    }

    private String getMonth(String month) {
	if (month.equals("Jan")) {
	    return "1";
	}
	if (month.equals("Feb")) {
	    return "2";
	}
	if (month.equals("Mar")) {
	    return "3";
	}
	if (month.equals("Apr")) {
	    return "4";
	}
	if (month.equals("May")) {
	    return "5";
	}
	if (month.equals("Jun")) {
	    return "6";
	}
	if (month.equals("Jul")) {
	    return "7";
	}
	if (month.equals("Aug")) {
	    return "8";
	}
	if (month.equals("Sep")) {
	    return "9";
	}
	if (month.equals("Oct")) {
	    return "10";
	}
	if (month.equals("Nov")) {
	    return "11";
	}
	if (month.equals("Dec")) {
	    return "12";
	}
	return "0";
    }

    private List<String> extractToList(byte[] dataIn) {
	int StartIndex = 0;
	int endIndex = -1;
	List<String> elements = new ArrayList<String>();

	for (int i = 0; i < dataIn.length; i++) {
	    if (dataIn[i] == ',') {
		endIndex = i - 1;
		elements.add(new String(dataIn, StartIndex, endIndex - StartIndex + 1));
		StartIndex = i + 1;
	    }
	}

	return elements;
    }

    /**
     * @param systemAnimal
     */
    private MidletAnimal copySystemAnimalToMidletAnimal(Animal systemAnimal) {
	MidletAnimal midletAnimal = new MidletAnimal();
	midletAnimal.setId(systemAnimal.getId());
	midletAnimal.setEarTag(systemAnimal.getEarTag());
	midletAnimal.setWeight(systemAnimal.getWeight());
	midletAnimal.setName(systemAnimal.getName());
	midletAnimal.setDateOfBirth(systemAnimal.getDateOfBirth());
	midletAnimal.setKraal(systemAnimal.getKraal().getName());
	midletAnimal.setPurchaseDate(systemAnimal.getPurchaseDate());
	midletAnimal.setDescription(systemAnimal.getDescription());
	midletAnimal.setBirthWeight(systemAnimal.getBirthWeight());
	midletAnimal.setSellingPrice(systemAnimal.getSellingPrice());
	midletAnimal.setBuyingPrice(systemAnimal.getBuyingPrice());
	return midletAnimal;
    }

    /**
     * @param request
     * @return
     * @throws IOException
     */
    private String getInputData(HttpServletRequest request) throws IOException {
	byte[] inData = null;

	inData = extractMidletData(request);

	ByteArrayInputStream bais = new ByteArrayInputStream(inData);
	DataInputStream dis = new DataInputStream(bais);

	String data = dis.readUTF();

	bais.reset();
	dis.close();
	bais.close();
	return data;
    }

    /**
     * @param request
     * @return
     * @throws IOException
     */
    private byte[] extractMidletData(HttpServletRequest request) throws IOException {
	byte[] inData;
	try {
	    int readBytes = -1;
	    int length = request.getContentLength();
	    in = request.getInputStream();
	    byte[] buffer = new byte[length];
	    bout = new ByteArrayOutputStream(length);
	    while ((readBytes = in.read(buffer, 0, length)) != -1) {
		bout.write(buffer, 0, readBytes);
	    }
	    bout.flush();
	    inData = bout.toByteArray();
	} finally {
	    if (in != null) {
		in.close();
	    }
	    if (bout != null) {
		bout.close();
	    }
	}
	return inData;
    }

    /**
     * @param response
     * @param midlet
     * @throws IOException
     */
    private void SendDataBackToMidlet(HttpServletResponse response, byte[] outData)
	    throws IOException {
	response.setContentType("application/octet-stream");
	OutputStream out = null;
	try {
	    out = response.getOutputStream();
	    for (int i = 0; i < outData.length; i++) {
		out.write(outData[i]);
	    }
	} finally {
	    out.close();
	}
    }

    private Concept getConcept(String name) {
	return conceptService.getConceptByName(name);
    }

    @RequestMapping(value = "/report/farm", method = RequestMethod.POST)
    public void farmReport(HttpServletRequest request, HttpServletResponse response)
	    throws IOException {
	List<Farm> farms = farmService.getFarms();
	SendDataBackToMidlet(response, prepareFarms(farms, farms.size()));
    }

    /**
     * @param farms
     * @return
     * @throws IOException
     */
    private byte[] prepareFarms(List<Farm> farms, int size) throws IOException {
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	DataOutputStream dos = new DataOutputStream(baos);

	String recordValue = "";

	if (size > 0) {
	    dos.writeUTF("yes");
	    dos.flush();
	    for (Farm farm : farms) {
		recordValue += farm.getName() + "," + farm.getAddress().getEmail() + "!"
			    + farm.getAddress().getPhoneNo() + "!"
			+ farm.getAddress().getPhysicalAddress()
			    + "!," + farm.getManager().getFullNames() + ","
			+ farm.getKraals().size()
			    + ",:";
	    }

	    dos.writeUTF(recordValue);
	    dos.flush();
	    recordValue = "";
	} else {
	    dos.writeUTF("noFarm");
	    dos.flush();

	}

	dos.close();
	baos.close();

	return baos.toByteArray();
    }

    @RequestMapping(value = "/report/kraal", method = RequestMethod.POST)
    public void KraalReport(HttpServletRequest request, HttpServletResponse response)
	    throws IOException {
	List<Kraal> kraals = farmService.getKraals();
	if (kraals.size() > 0) {
	    SendDataBackToMidlet(response, prepareKraal(kraals));
	} else {
	    SendDataBackToMidlet(response, writeIntoBytes("noKraal").toByteArray());
	}
    }

    /**
     * @param kraals
     * @return
     */
    private byte[] prepareKraal(List<Kraal> kraals) throws IOException {
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	DataOutputStream dos = new DataOutputStream(baos);

	String recordValue = "";
	for (Kraal kraal : kraals) {
	    recordValue += kraal.getName() + "," + kraal.getFarm().getName() + ","
		    + kraal.getAnimals().size() + ",:";
	}
	dos.writeUTF(recordValue);
	dos.flush();
	recordValue = "";

	dos.close();
	baos.close();

	return baos.toByteArray();
    }

    @RequestMapping(value = "/kraal/names", method = RequestMethod.POST)
    public void kraalNames(HttpServletRequest request, HttpServletResponse response)
	    throws IOException {
	List<Kraal> kraals = farmService.getKraals();
	SendDataBackToMidlet(response, prepareKraalNames(kraals, kraals.size()));
    }

    private byte[] prepareKraalNames(List<Kraal> kraals, int size) throws IOException {
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	DataOutputStream dos = new DataOutputStream(baos);

	if (size > 0) {
	    String recordValue = "";
	    for (Kraal kraal : kraals) {
		recordValue += kraal.getName() + ":";
	    }
	    dos.writeUTF(recordValue);
	    dos.flush();
	    recordValue = "";

	} else {
	    dos.writeUTF("noNames");
	    dos.flush();
	}

	dos.close();
	baos.close();

	return baos.toByteArray();
    }
}
